#include <common.h>
#include <process.h>
#include <thread.h>

/*////////////////////////////////
//		SYSCALLS
//////////////////////////////////
		GROUPS
		0x100	Process functions
		0x200	File functions
		0x300	OS functions
		0x400	v86 functions
		0x500	Memory functions

//////////////////////////////////
//		0x100	process functions
//////////////////////////////////
	fork			eax = 0x100
			Creates a copy of the current process.
		Arguments:
			nothing
		Returns:
			eax - 0 for child, new pid for parent

	exit			eax = 0x101
			Destroy a process
		Arguments:
			nothing
		Returns:
			does not return

	done			eax = 0x102
			Destroys a thread but leaves the process
		Arguments:
			nothing
		Returns:
			does not return

	getpid			eax = 0x103
			Get current process ID
		Arguments:
			nothing
		Returns:
			eax - Process ID

	getppid			eax = 0x104
			Get parent process ID
		Arguments:
			nothing
		Returns:
			eax - Parent process ID

	signal			eax = 0x105
			Send a signal to a process
		Arguments:
			ebx - process id
			ecx - signal number
		Returns:
			nothing

	yield			eax = 0x106
			Give upp timeslice
		Arguments:
			nothing
		Returns:
			nothing
			
	regirq		eax = 0x107
			Register as a handler for an irq.
		Arguments:
			ebx - irq number
		Returns:
			nothing

	exec		eax = 0x108
			Run a process
		Arguments:
			ebx - process image address
			ecx - argument list
		Returns:
			nothing
			
	setmsg		eax = 0x109
			Set message pointer
		Arguments:
			ebx - virtual address of message (page alligned)
		Returns:
			eax - 0 on success, 1 on failure
	
	getmsg		eax = 0x10A
			Get message pointer
		Arguments:
			ebx - virtual address of message buffer
		Returns:
			eax - 0 on success, 1 on failure

	name		eax = 0x10B
			Set process name
		Arguments:
			ebx - pointer to name string
		Returns:
			nothing

//////////////////////////////////
//		0x400	v86 functions
//////////////////////////////////
	v86_int			eax = 0x400
			Execute an interrupt in v86 mode
		Arguments:
			ebx - Interrupt number
			ecx - Registers pointer
		Returns:
			nothing

	v86_getbuf		eax = 0x401
			Set up a buffer page for transfering data to/from v86 mode
		Arguments:
			nothing
		Returns:
			eax - pointer to buffer

//////////////////////////////////
//		0x500	memory functions
//////////////////////////////////
	map				eax = 0x500
			Maps a piece of memory
		Arguments:
			ebx - virtual address
			ecx - flags
		Returns:
			eax - virtual address or 0 on failure

	unmap			eax = 0x501
			Unmaps a piece of memory
		Arguments:
			ebx - virtual address
		Returns:
			nothing
			
	mapset			eax = 0x502
			Map a set memory location
		Arguments:
			ebx - virtual address
			ecx - physical address
			edx - flags
		Returns:
			nothing


//////////////////////////////////
//////////////////////////////////
//		0xF00	Temporary functions
//////////////////////////////////
	putch			eax = 0xF00
			Print a character to screen
		Arguments:
			ebx - Character
		Returns:
			nothing

*/
unsigned int irq_handlers[256];
registers *irq_sender(registers *r)
{
	if(irq_handlers[r->int_no] != 0)
	{
		asm("cli");
		thread *t = (thread *)signal_thread(irq_handlers[r->int_no], 0x1,0,r);
		skip_to_thread(t,r);
	}
}

void syscall_fork(registers *r)
{
	asm("cli");
	thread *t = (thread *)_fork(r);
	skip_to_thread(t,r);

}
void syscall_exit(registers *r)
{
	asm("cli");
	process *p = current_thread->process;
	unsigned int i = 0;
	while(i<256)
	{
		if(p->threads[i])
			p->threads[i]->state = TH_ZOMBIE;
		i++;
	}
	current_thread->state = TH_ZOMBIE;
	schedule(r);
	i = 0;
	while(i<256)
	{
		if(p->threads[i])
			destroy_thread(p->threads[i]);
		i++;
	}
	
	i = 0;
	while(i<256)
	{
		if(irq_handlers[i] == p->pid)
		{
			register_int_handler(i,0);
			irq_handlers[i] = 0;
		}
		i++;
	}
	kfree(p);

//	destroy_process(p);
	// Destroy process structure here.
}
void syscall_done(registers *r)
{
	asm("cli");
	thread *t = current_thread;
	current_thread->state = TH_ZOMBIE;
	schedule(r);
	destroy_thread(t);
}
void syscall_getpid(registers *r)
{
	r->eax = current_thread->process->pid;
}
void syscall_getppid(registers *r)
{
	r->eax = current_thread->process->ppid;
}
void syscall_signal(registers *r)
{
	thread *t = (thread *)signal_thread(r->ebx, r->ecx,current_thread->process->pid, r);
	skip_to_thread(t,r);
}
void syscall_regirq(registers *r)
{
	register_int_handler(r->ebx,&irq_sender);
	irq_handlers[r->ebx] = current_thread->process->pid;
}
void syscall_v86int(registers *r)
{
	_v86_int(r,r->ebx, r->ecx);
}
void syscall_map(registers *r)
{
	map(r->ebx, pmm_alloc_page(), r->ecx);
}
void syscall_unmap(registers *r)
{
	unsigned int page;
	get_mapping(r->ebx, &page);
	pmm_free_page(page);
	unmap(r->ebx);
}
void syscall_exec(registers *r)
{
	unsigned int start = r->ebx;
	unsigned int addr = elf_load((unsigned char *)start);
	unsigned int *stack = (unsigned int *)r->useresp;
	*--stack= (unsigned int)r->ecx;
	r->useresp = (unsigned int)stack;
	r->ebp = (unsigned int)stack;
	r->eip = addr;
}
void syscall_setmsg(registers *r)
{
	r->eax = 1;
	if(current_thread->message != 0)
	{
		pmm_free_page(current_thread->message&~0xFFF);
	}
	if(get_mapping(r->ebx,&current_thread->message))
	{
		unmap(r->ebx);
		map(r->ebx,pmm_alloc_page(),PAGE_USER | PAGE_WRITE | PAGE_PRESENT);
	//	r->eax = 0;
	}
}
void syscall_getmsg(registers *r)
{
	r->eax = 1;
	if(current_thread->message)
	{
		if(get_mapping(r->ebx,&r->eax))
			unmap(r->ebx);
		map(r->ebx,current_thread->message, PAGE_PRESENT | PAGE_WRITE | PAGE_USER);
	//	r->eax = 0;
	}
}
void syscall_setmap(registers *r)
{
	map(r->ebx, r->ecx, r->edx);
}

void syscalls(registers *r)
{
	unsigned int i;
	process *p = 0;
	thread *t = 0;
	switch(r->eax)
	{
		case 0x100:
			syscall_fork(r);
			break;
		case 0x101:
			//exit
			syscall_exit(r);
			break;
		case 0x102:
			//done
			syscall_done(r);
			break;
		case 0x103:
			//Get pid
			syscall_getpid(r);
			break;
		case 0x104:
			//Get ppid
			syscall_getppid(r);
			break;
		case 0x105:
			//signal
			syscall_signal(r);
			break;
		case 0x106:
			//yield
			schedule(r);
			break;
		case 0x107:
			//regirq
			syscall_regirq(r);
			break;
		case 0x108:
			//exec
			syscall_exec(r);
			break;
		case 0x109:
			syscall_setmsg(r);
			break;
		case 0x10A:
			syscall_getmsg(r);
			break;
		case 0x400:
			//v86 int
			syscall_v86int(r);
			break;
		case 0x401:
			_v86_getbuf(r);
			break;
		case 0x500:
			syscall_map(r);
			break;
		case 0x501:
			syscall_unmap(r);
			break;
		case 0x502:
			syscall_setmap(r);
			break;
		case 0xF00:
			//temp printf
			printf("%c",r->ebx);
			break;
		case 0xF01:
			// temp exec
			r->eip = r->ebx;
			break;
		default:
		return;//r->eax = 0;
	}
}

void init_syscalls()
{
	register_int_handler(0xFF, &syscalls);
	register_int_handler(0x80, &syscalls);
	memset(&irq_handlers,0,sizeof(unsigned int)*256);
}
